package com.lucas.springboot_restful_api.core;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.lucas.springboot_restful_api.core.configuration.ExceptionCodeConfiguration;
import com.lucas.springboot_restful_api.exception.http.HttpException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;

@RestControllerAdvice
public class GlobalExceptionAdvice {

    @Autowired
    private ExceptionCodeConfiguration codes;

    //    ================== 底层错误 =================
    @ExceptionHandler(Exception.class)
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public UnifyResponse handleException(HttpServletRequest req, Exception e) {
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        UnifyResponse message = new UnifyResponse(9999, "服务器未知异常", method + " " + requestUrl);
        e.printStackTrace();
        return message;
    }

    //    ================== 请求方法错误 =================
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(code = HttpStatus.METHOD_NOT_ALLOWED)
    public UnifyResponse handleHttpMessageNotReadableException(HttpServletRequest req, HttpRequestMethodNotSupportedException e) {
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        String supportedMessage = StringUtils.join(e.getSupportedMethods(), ",");
        UnifyResponse response = UnifyResponse.builder()
                .err_code(10002)
                .err_msg(codes.getMessage(10002).concat(";").concat("支持的请求方法:").concat(supportedMessage))
                .request(method + " " + requestUrl)
                .build();
        e.printStackTrace();
        return response;
    }

    //    ================== 自定义错误 =================
    @ExceptionHandler(HttpException.class)
    public ResponseEntity<UnifyResponse> handleHttpException(HttpServletRequest req, HttpException e){
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();

//        ResponseEntity
        UnifyResponse message = new UnifyResponse(e.getCode(),codes.getMessage(e.getCode()), method + " " + requestUrl);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());

        ResponseEntity<UnifyResponse> r = new ResponseEntity<>(message, headers, httpStatus);
        return r;
    }

    //    ================== 参数错误 =================
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public UnifyResponse handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e) {
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<ObjectError> objectErrors = e.getBindingResult().getAllErrors();
        StringBuffer message = new StringBuffer();
        fieldErrors.forEach(error -> {
            message.append(error.getField()).append(":").append(error.getDefaultMessage()).append(";");
        });
        objectErrors.forEach(error -> {
            message.append(error.getDefaultMessage()).append(";");
        });
        UnifyResponse response = new UnifyResponse(10000, message.toString(), method + " " + requestUrl);
        e.printStackTrace();
        return response;
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public UnifyResponse handleConstraintViolationException(HttpServletRequest req, ConstraintViolationException e) {
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        StringBuffer message = new StringBuffer();
        for (ConstraintViolation error : e.getConstraintViolations()) {
            String[] str = error.getPropertyPath().toString().split("\\.");
            String paramName = str[str.length - 1];
            message.append(paramName).append(":").append(error.getMessage()).append(";");
        }
        UnifyResponse response = new UnifyResponse(10000, message.toString(), method + " " + requestUrl);
        e.printStackTrace();
        return response;
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public UnifyResponse handleHttpMessageNotReadableException(HttpServletRequest req, HttpMessageNotReadableException e) {
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        UnifyResponse response = new UnifyResponse(10000, codes.getMessage(10000), method + " " + requestUrl);
        e.printStackTrace();
        return response;
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public UnifyResponse handleMissingServletRequestParameterException(HttpServletRequest req, MissingServletRequestParameterException e) {
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        UnifyResponse response = new UnifyResponse(10002, codes.getMessage(10002), method + " " + requestUrl);
        e.printStackTrace();
        return response;
    }

    @ExceptionHandler(BindException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public UnifyResponse handleBindException(HttpServletRequest req, BindException e) {
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        StringBuffer message = new StringBuffer();
        e.getFieldErrors().forEach(fieldError -> {
            message.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(";");
        });
        UnifyResponse response = UnifyResponse.builder()
                .err_code(10000)
                .err_msg(message.toString())
                .request(method + " " + requestUrl)
                .build();
        e.printStackTrace();
        return response;
    }

    @ExceptionHandler(InvalidFormatException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public UnifyResponse handleInvalidFormatException(HttpServletRequest req, InvalidFormatException e) {
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        StringBuffer message = new StringBuffer();
        UnifyResponse response = UnifyResponse.builder()
                .err_code(10000)
                .err_msg(message.toString())
                .request(method + " " + requestUrl)
                .build();
        e.printStackTrace();
        return response;
    }
}
